package org.openglcharts.common;

import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Vector;

public class ValueContainer implements Serializable {
	private static final long serialVersionUID = -1349812606540918667L;
	private Vector<Value> points = new Vector<Value>();

	public void add(Value value) {
		if (this.points.isEmpty() || this.lastElement().x <= value.x) {
			this.points.add(value);
		} else {
			int closestIndex = this.getFuzzy(value.x);
			if (this.points.get(closestIndex).x >= value.x) {
				this.points.add(closestIndex, value);
			} else {
				this.points.add(closestIndex + 1, value);
			}
		}
	}

	public int getFuzzy(float xPosition) {
		int searchIntervalStart = 0;
		int searchIntervalEnd = this.points.size() - 1;
		int median = searchIntervalEnd / 2;

		while (searchIntervalEnd - searchIntervalStart > 1) {
			if (this.points.get(median).x < xPosition)
				searchIntervalStart = median;
			else
				searchIntervalEnd = median;
			median = searchIntervalStart
					+ (searchIntervalEnd - searchIntervalStart) / 2;
		}

		if (Math.abs(this.points.get(searchIntervalStart).x - xPosition) > Math
				.abs(this.points.get(searchIntervalEnd).x - xPosition))
			return searchIntervalEnd;
		else
			return searchIntervalStart;
	}
	
	public Value get(int index) {
		return this.points.get(index);
	}
	
	public Iterator<Value> iterator() {
		return new PointContainerIterator(this.points);
	}
	
	public Iterator<Value> iteratorSubset(int startPositionX, int endPositionX) {
		int indexStart = this.getFuzzy(startPositionX);
		if(indexStart > 0) indexStart -= 1;
		int indexEnd = this.getFuzzy(endPositionX);
		if(indexEnd < this.points.size() - 1) indexEnd += 1;
		return new PointContainerIterator(this.points, indexStart, indexEnd);
	}
	
	public Value lastElement() {
		if (!this.points.isEmpty()) {
			return this.points.get(this.points.size() - 1);
		} else {
			return null;
		}
	}

	public Value firstElement() {
		if (!this.points.isEmpty()) {
			return this.points.get(0);
		} else {
			return null;
		}
	}
	
	public boolean isEmpty() {
		return this.points.isEmpty();
	}
	
	public int size()
	{
		return this.points.size();
	}
	
	private class PointContainerIterator implements Iterator<Value> {

		private Vector<Value> points;
		private int cursor;
		private int subsetEnd;

		public PointContainerIterator(Vector<Value> points) {
			this.points = points;
			this.cursor = 0;
			this.subsetEnd = points.size() - 1;
		}

		public PointContainerIterator(Vector<Value> points, int subsetBegin, int subsetEnd) {
			this(points);
			this.cursor = subsetBegin;
			this.subsetEnd = subsetEnd;
		}

		@Override
		public boolean hasNext() {
			return this.cursor < (this.points.size() - 1) && this.cursor < this.subsetEnd;
		}

		@Override
		public Value next() {
			if (! this.hasNext())
	            throw new NoSuchElementException();
	        return this.points.get(this.cursor++);
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}
	}
}
